Sound

produces a note of specified frequency and duration.

The Sound routine generates squarewave tones with frequencies of 280 Hz to 71 kHz and durations of 3.6 ms to 0.92 seconds at 4 MHz. It differs from its PBASIC counterpart in that all frequency values from 1 to 255 produce squarewave tones. PBASIC's Sound instruction generates tones when the frequency is in the range of 1 to 127, and hissing white-noise effects from 128 to 255. Both PBASIC and assembly versions of Sound produce a silent pause with a frequency value of 0.

To use the routine, store a frequency value from 0 to 255 in freq, and a duration from 0 to 255 in duratn. Put the output pin number (0 to 7) into pin and the port number (0 to 2 for RA through RC) into the w register, and call Sound.

The following formula determines the frequency output by Sound (assuming a PIC clock speed of 4 MHz):

The formula shows that freq sets the period of the tone in 14-µs units. As a result, the frequency isn't a linear function of freq. For example, a freq of 1 produces a 280.1-Hz tone, while 2 produces 281.2 Hz. At the higher end of the scale, a freq of 250 produces an 11.9-kHz tone, while 251 produces 14.29 kHz.

PBASIC's Sound command has the same characteristic, but starts slightly below 100 Hz. It has a period resolution of approximately 83µs. If you want to more closely emulate PBASIC, just pad the :loop of this Sound routine to take a total of 83µs.

The PBASIC command permits you to specify a list of frequencies and durations to be played. The demonstration program shows how to do this in assembly by using a pair of lookup tables. If you need white-noise effects in your program, use the Random routine. Call Random from within a tight program loop, and copy one bit of its shift register to the speaker output. The random bit stream will produce a hissing sound.

One hint on using Sound: always assign the port number to w immediately before calling the routine. If you don't, some other instruction that uses w may change its contents, causing an error.

Demonstrating Sound.

To see Sound in operation, connect the circuit below to an erasable PIC or PIC emulator, such as the Parallax downloader. Assemble and run SOUND.SRC. When you apply power to the PIC or reset the emulator, the speaker will play a short tune.


;
; ***************************************************************************
; ***  Bubble Software Parallax to PIC Source Converter. Copyright 1999.  ***
; ***  http://www.bubblesoftonline.com                 email: sales@picnpoke.com  ***
; ***************************************************************************
;
; SOUND port, pin, frequency, duration
; Generates squarewave tones (notes) of the specified frequency and
; duration. This demonstration program shows how to use lookup tables to
; play tunes or effects from data stored in program memory.

; Device data and reset vector
	P = pic16c55
	#include <16c55.inc>   ; processor assembler definitions
	_CONFIG _xt_osc & _wdt_off & _protect_off
 reset start

 	org 8
pin 	Res 	d'1' 	; Pin number to use as output (0-7).
freq 	Res 	d'1' 	; Frequency of note.
duratn 	Res 	d'1' 	; Duration of note.
f_copy 	Res 	d'1' 	; Copy of freq variable.
temp 	Res 	d'1' 	; Variable to stretch duratn to 16 bits.
index 	Res 	d'1' 	; Index to tables of notes, lengths.

 	org 0

; Table to convert pin number (0-7) into bit mask (00000001b to 10000000b).
Pinz         ADDWF pcl                  
             RETLW d'1'                 
             RETLW d'2'
             RETLW d'4'
             RETLW d'8'
             RETLW d'16'
             RETLW d'32'
             RETLW d'64'
             RETLW d'128'

; Table of notes for the demonstration tune "Charge."
Notes        ADDWF pcl                  
             RETLW d'166'               
             RETLW d'189'
             RETLW d'204'
             RETLW d'212'
             RETLW d'204'
             RETLW d'212'

; Table of note durations for the demonstration tune "Charge."
Lengths      ADDWF pcl                  
             RETLW d'52'                
             RETLW d'52'
             RETLW d'52'
             RETLW d'82'
             RETLW d'45'
             RETLW d'190'

start        MOVLW d'0'                 ; Output for speaker.
             TRIS 5h
             CLRF temp                  ; Clear LSB of duratn counter.
             CLRF index                 ; Clear the note counter.
start_loop   MOVF index,w               ; Look up note in table.
             CALL Notes                 
             MOVWF freq                 ; Store note in freq.
             MOVF index,w               ; Look up length in table.
             CALL Lengths               
             MOVWF duratn               ; Store in duratn.
             MOVLW d'3'                 ; Select pin 3 of port
             MOVWF pin
             MOVLW d'0'                 ; RA for speaker output
             CALL Sound                 ; Play the sound.
             INCF index                 ; Advance to next sound in tables.
             MOVLW ~d'5'                ; IF index <= 5, play note
             ADDWF index,w
             BTFSS status,c
             GOTO start_loop
             GOTO $                     ; ELSE end.

; Upon entry, the desired speaker pin must already be set up as an output.
; The w register contains a number representing the output port (0-2) for
; RA through RC. Variable "pin" contains the pin number (0-7). The variables
; freq and duratn are the frequency and duration of the note.

; Upon exit, duratn will be cleared to 0, but freq will be intact. If you require the
; value of duratn for some other purpose, copy it before calling Sound. The
; variable pin and the file-select register (fsr) will also have be altered.

Sound        COMF duratn                ; Convert to two's complement.
             INCF duratn                
             MOVWF fsr                  ; Point to the port number.
             MOVLW 5h                   ; Add offset for port RA.
             ADDWF fsr
             MOVF pin,w                 ; Now look up pin number in
             CALL Pinz                  ; table and get bit mask.
             MOVWF pin                  ; Put the mask into pin.
             MOVF freq,w                ; Use copy so freq value is saved.
             MOVWF f_copy
             BTFSC status,z             ; If freq is 0, clear pin to
             CLRF pin                   ; produce a silent delay.
Sound_loop   MOVF pin,w                 ; f_copy=f_copy+1: IF f_copy<=255
             INCFSZ f_copy              ; THEN w=pin ELSE w=0
             CLRW                       ; (makes a silent delay if freq = 0)
             XORWF indirect             ; indirect=indirect XOR w
             MOVF f_copy,w              ; If f_copy has rolled over to 0,
             BTFSC status,z             ; reload it with the value of freq.
             MOVF freq,w                
             MOVWF f_copy               
             INCF temp                  ; Increment LSB of duratn counter.
             BTFSC status,z             ; Overflow? Yes: Increment duratn and
             INCFSZ duratn              ; if it overflows, return. Else loop.
             GOTO Sound_loop            
             RETLW 0h                   

 end
             
             
             end


; SOUND port, pin, frequency, duration ; Generates squarewave tones (notes) of the specified frequency and ; duration. This demonstration program shows how to use lookup tables to ; play tunes or effects from data stored in program memory. org 8 pin ds 1 ; Pin number to use as output (0-7). freq ds 1 ; Frequency of note. duratn ds 1 ; Duration of note. f_copy ds 1 ; Copy of freq variable. temp ds 1 ; Variable to stretch duratn to 16 bits. index ds 1 ; Index to tables of notes, lengths. ; Device data and reset vector device pic16c55,xt_osc,wdt_off,protect_off reset start org 0 ; Table to convert pin number (0-7) into bit mask (00000001b to 10000000b). Pinz jmp pc+w retw 1,2,4,8,16,32,64,128 ; Table of notes for the demonstration tune "Charge." Notes jmp pc+w retw 166,189,204,212,204,212 ; Table of note durations for the demonstration tune "Charge." Lengths jmp pc+w retw 52,52,52,82,45,190 start mov !ra, #0 ; Output for speaker. clr temp ; Clear LSB of duratn counter. clr index ; Clear the note counter. :loop mov w,index ; Look up note in table. call Notes mov freq,w ; Store note in freq. mov w,index ; Look up length in table. call Lengths mov duratn,w ; Store in duratn. mov pin,#3 ; Select pin 3 of port mov w,#0 ; RA for speaker output call Sound ; Play the sound. inc index ; Advance to next sound in tables. cjbe index,#5,:loop ; IF index <= 5, play note jmp $ ; ELSE end. ; Upon entry, the desired speaker pin must already be set up as an output. ; The w register contains a number representing the output port (0-2) for ; RA through RC. Variable "pin" contains the pin number (0-7). The variables ; freq and duratn are the frequency and duration of the note. ; Upon exit, duratn will be cleared to 0, but freq will be intact. If you require the ; value of duratn for some other purpose, copy it before calling Sound. The ; variable pin and the file-select register (fsr) will also have be altered. Sound NEG duratn ; Convert to two's complement. mov fsr,w ; Point to the port number. add fsr,#RA ; Add offset for port RA. mov w,pin ; Now look up pin number in call Pinz ; table and get bit mask. mov pin,w ; Put the mask into pin. mov f_copy,freq ; Use copy so freq value is saved. snz ; If freq is 0, clear pin to clr pin ; produce a silent delay. :loop mov w,pin ; f_copy=f_copy+1: IF f_copy<=255 incsz f_copy ; THEN w=pin ELSE w=0 clr w ; (makes a silent delay if freq = 0) XOR indirect,w ; indirect=indirect XOR w mov w,f_copy ; If f_copy has rolled over to 0, snz ; reload it with the value of freq. mov w,freq mov f_copy,w inc temp ; Increment LSB of duratn counter. snz ; Overflow? Yes: Increment duratn and incsz duratn ; if it overflows, return. Else loop. jmp :loop ret

See also: